#include "AffectsBIP.h"
#include <vector>
#include "PKB.h"

AffectsBIP::AffectsBIP(){
}
AffectsBIP::~AffectsBIP(){
}
BOOLEAN AffectsBIP::GetAffectsBIP(STMT affecting, STMT affected)
{
	if (affecting.node_type == ASSIGN && affected.node_type == ASSIGN)
	{
		if(PKB::nextbip.IsTransitiveNextBIP(affecting.statement_number,affected.statement_number))
		{
			STMT_ARRAY getAffected = AffectsBIP::RetrieveAffectedAssignmentBIP(affecting);
			if(getAffected.find(affected) != getAffected.end())
				return true;
		}
	}
	return false;
}
STMT_ARRAY AffectsBIP::RetrieveAffectedAssignmentBIP(STMT affecting)
{
	STMT_ARRAY result;
	PROG_LINE_ARRAY temp,temp1,temp2,tempstore;
	// Get variable index that is modified by affecting statement
	VARINDEX modVar = PKB::modifies.GetVariableModifiedByAssignment(affecting);
	// Get next relation of affecting statement
	PROG_LINE_ARRAY next = PKB::nextbip.GetNextBIP(affecting.statement_number);
	// check if affecting is an assignment stmt
	if (PKB::shortcutTable.GetStatementType(affecting.statement_number) == ASSIGN)
	{
		// Iterates through the relations 
		while (next.size() != 0){
			for(auto it=next.begin();it!=next.end();it++)
			{
				if(PKB::shortcutTable.GetStatementType(*it) == ASSIGN){
					STMT stmt;
					stmt.node_type = ASSIGN;
					stmt.statement_number = *it;
					// if stmt is affected
					if (PKB::uses.GetAssignmentUses(stmt,modVar)){
						result.insert(stmt);
						// assignment statement modifies and uses the variable
						if(PKB::modifies.GetAssignmentModifies(stmt,modVar) == false){
							temp.insert(*it);
							temp1.insert(*it);
						}
					}
					else if (PKB::modifies.GetAssignmentModifies(stmt,modVar))
						continue;
					else{
						temp.insert(*it);
						temp1.insert(*it);
					}
				}	
				else {
					temp.insert(*it);
					temp1.insert(*it);
				}
			}
		
			vector<PROG_LINE> vtemp (PKB::shortcutTable.GetStatementCount());
			next.clear(); 
			// Prepare next set of next relation to search for
			for(auto it=temp.begin();it!=temp.end();it++)
			{
				temp2 = PKB::nextbip.GetNextBIP(*it);
				for(auto it1=temp2.begin();it1!= temp2.end();it1++)
				{
					if(temp1.find(*it1) == temp1.end())
						tempstore.insert(*it1);
				}
				set_union(tempstore.begin(),tempstore.end(),next.begin(),next.end(),vtemp.begin());
				next = PKB::next.VectorToSet(vtemp);
			}
			tempstore.clear();
			temp.clear();
		}
	}

	return result;
}


STMT_ARRAY AffectsBIP::RetrieveAffectingAssignmentBIP(STMT affected)
{
	STMT_ARRAY result;
	PROG_LINE_ARRAY temp,temp1,temp2,tempstore;
	if (affected.node_type == ASSIGN)
	{
		VARINDEX_ARRAY varIndexes = PKB::uses.GetVariablesUsedByAssignment(affected);
		PROG_LINE_ARRAY next = PKB::nextbip.GetPreviousBIP(affected.statement_number);
		
		while (next.size() != 0 && varIndexes.size()>0){
			for(auto it=next.begin();it!=next.end();it++)
			{
				if(PKB::shortcutTable.GetStatementType(*it) == ASSIGN){
					STMT stmt;
					stmt.node_type = ASSIGN;
					stmt.statement_number = *it;
					VARINDEX var = PKB::modifies.GetVariableModifiedByAssignment(stmt);
					if (varIndexes.find(var) != varIndexes.end()){
						result.insert(stmt);
						temp.insert(*it);
						temp1.insert(*it);
						varIndexes.erase(var);
					}
					else{
						temp.insert(*it);
						temp1.insert(*it);
					}
				}
				// encounter calls statement
				else if (PKB::shortcutTable.GetStatementType(*it) == CALL)
				{
					vector<VARINDEX> vIndex(PKB::varTable.GetAllVarNames().size());
					// if called procedure modifies the variable
					VARINDEX_ARRAY procVar = PKB::modifies.GetVariablesModifiedByProcedure(PKB::procTable.GetProcIndex(affected.statement_number));
					set_intersection(procVar.begin(),procVar.end(),varIndexes.begin(),varIndexes.end(),vIndex.begin());
					if (vIndex.size() > 0){
						procVar = PKB::next.VectorToSet(vIndex);
						vIndex.clear();
						set_difference(varIndexes.begin(),varIndexes.end(),procVar.begin(),procVar.end(),vIndex.begin());
						varIndexes = PKB::next.VectorToSet(vIndex);
					}
					else {
						temp.insert(*it);
						temp1.insert(*it);
					}
				}
				else{
					temp.insert(*it);
					temp1.insert(*it);
				}

			}
			vector<PROG_LINE> vtemp (PKB::shortcutTable.GetStatementCount());
			next.clear(); 
			// Prepare next set of next relation to search for
			for(auto it=temp.begin();it!=temp.end();it++)
			{
				temp2 = PKB::nextbip.GetPreviousBIP(*it);
				for(auto it1=temp2.begin();it1!= temp2.end();it1++)
				{
					if(temp1.find(*it1) == temp1.end())
						tempstore.insert(*it1);
				}
				set_union(tempstore.begin(),tempstore.end(),next.begin(),next.end(),vtemp.begin());
				next = PKB::next.VectorToSet(vtemp);
			}
			tempstore.clear();
			temp.clear();
		}
	}
	return result;
}

BOOLEAN AffectsBIP::GetTransitiveAffectsBIP(STMT affecting, STMT affected)
{
	if (affecting.node_type == ASSIGN && affected.node_type == ASSIGN)
	{
		if(PKB::nextbip.IsTransitiveNextBIP(affecting.statement_number,affected.statement_number))
		{
			
			STMT_ARRAY getAffecting = AffectsBIP::RetrieveTransitiveAffectedAssignmentBIP(affecting);
			if(getAffecting.find(affected) != getAffecting.end())
				return true;
		}
	}	
	return false;
}
STMT_ARRAY AffectsBIP::RetrieveTransitiveAffectedAssignmentBIP(STMT affecting)
{	
	STMT_ARRAY result,next, temp,temp1;
	next = AffectsBIP::RetrieveAffectedAssignmentBIP(affecting);
	while(next.size() > 0)
	{
		vector<STMT> vtemp(PKB::shortcutTable.GetStatementCount());
		set_union(next.begin(),next.end(),result.begin(),result.end(),vtemp.begin());
		result = AffectsBIP::VectorToSet(vtemp);

		// Compute next set of next relation
		vtemp = vector<STMT>(PKB::shortcutTable.GetStatementCount());
		for (auto it=next.begin();it!=next.end();it++)
		{
			temp = AffectsBIP::RetrieveAffectedAssignmentBIP(*it);
			set_union(temp.begin(),temp.end(),temp1.begin(),temp1.end(),vtemp.begin());
			temp1 = AffectsBIP::VectorToSet(vtemp);
		}
		vtemp = vector<STMT>(PKB::shortcutTable.GetStatementCount());
		set_difference(temp1.begin(),temp1.end(),result.begin(),result.end(),vtemp.begin());
		next = AffectsBIP::VectorToSet(vtemp);
	}
	return result;
}
STMT_ARRAY AffectsBIP::RetrieveTransitiveAffectingAssignmentBIP(STMT affected)
{
	STMT_ARRAY result,next, temp,temp1;
	next = AffectsBIP::RetrieveAffectingAssignmentBIP(affected);
	while(next.size() > 0)
	{
		vector<STMT> vtemp(PKB::shortcutTable.GetStatementCount());
		set_union(next.begin(),next.end(),result.begin(),result.end(),vtemp.begin());
		result = AffectsBIP::VectorToSet(vtemp);

		// Compute next set of next relation
		vtemp = vector<STMT>(PKB::shortcutTable.GetStatementCount());
		for (auto it=next.begin();it!=next.end();it++)
		{
			temp = AffectsBIP::RetrieveAffectingAssignmentBIP(*it);
			set_union(temp.begin(),temp.end(),temp1.begin(),temp1.end(),vtemp.begin());
			temp1 = AffectsBIP::VectorToSet(vtemp);
		}
		vtemp = vector<STMT>(PKB::shortcutTable.GetStatementCount());
		set_difference(temp1.begin(),temp1.end(),result.begin(),result.end(),vtemp.begin());
		next = AffectsBIP::VectorToSet(vtemp);
	}
	return result;
}
STMT_ARRAY AffectsBIP::HelperGetTransitiveBIP(set<STMT> input,int operation){
	STMT_ARRAY temp;
	STMT_ARRAY temp1;
	
	vector<STMT> store(PKB::shortcutTable.GetStatementCount()); 
	for(auto it=input.begin();it!=input.end();it++){
		if (operation == 1)
			temp = AffectsBIP::RetrieveAffectedAssignmentBIP(*it);
		else
			temp = AffectsBIP::RetrieveAffectingAssignmentBIP(*it);
		set_union(temp1.begin(),temp1.end(),temp.begin(),temp.end(),store.begin());
		temp1 = AffectsBIP::VectorToSet(store);
	}
	return temp1;	
}
set<STMT> AffectsBIP::VectorToSet(vector<STMT> input){
	STMT_ARRAY result;
	for(auto it=input.begin();it!=input.end();it++)
		result.insert(*it);
	auto zero = result.begin();
	if (zero != result.end())
		result.erase(zero);
	return result;
}

/*STMT_ARRAY Affects::FindAffectedHelper(PROG_LINE_ARRAY current,VARINDEX modVar)
{
	STMT_ARRAY result, temp;
	vector<STMT> vtemp(PKB::shortcutTable.GetStatementCount());
	for(auto it=current.begin();it!=current.end();it++)
	{
		if(PKB::shortcutTable.GetStatementType(*it) == ASSIGN){
			STMT stmt;
			stmt.node_type = ASSIGN;
			stmt.statement_number = *it;
			// if stmt is affected
			if (PKB::uses.GetAssignmentUses(stmt,modVar)){
				result.insert(stmt);
				PROG_LINE_ARRAY next = PKB::next.GetNext(*it);
				if (next.size() > 0){
					temp = Affects::FindAffectedHelper(next,modVar);
					set_union(temp.begin(),temp.end(),result.begin(),result.end(),vtemp.begin());
					result = Affects::VectorToSet(vtemp);
				}
			}
			// stmt did not overwrites
			else if (PKB::modifies.GetAssignmentModifies(stmt,modVar) == false){
				PROG_LINE_ARRAY next = PKB::next.GetNext(*it);
				if (next.size() > 0){
					temp = Affects::FindAffectedHelper(PKB::next.GetNext(*it),modVar);
					set_union(temp.begin(),temp.end(),result.begin(),result.end(),vtemp.begin());
					result = Affects::VectorToSet(vtemp);
				}
			}
			// stmt overwrites
			else
				continue;
		}
		// encounter calls statement
		else if (PKB::shortcutTable.GetStatementType(*it) == CALL)
		{
			// if called procedure modifies the variable
			if (PKB::modifies.GetProcedureModifies(PKB::calls.GetCalleeByStatement(*it),modVar)){
				continue;
			}
			// continue checking
			else
			{	
				PROG_LINE_ARRAY next = PKB::next.GetNext(*it);
				if (next.size() > 0){
					temp = Affects::FindAffectedHelper(next,modVar);
					set_union(temp.begin(),temp.end(),result.begin(),result.end(),vtemp.begin());
					result = Affects::VectorToSet(vtemp);
				}
			}
		}
		// Any other statement
		else
		{
			PROG_LINE_ARRAY next = PKB::next.GetNext(*it);
			if (next.size() > 0){
				temp = Affects::FindAffectedHelper(next,modVar);
				set_union(temp.begin(),temp.end(),result.begin(),result.end(),vtemp.begin());
				result = Affects::VectorToSet(vtemp);
			}
		}
	}
	return result;
}*/